package com.matrix.app.flux.upload.controller;

import com.matrix.framework.core.common.result.Result;
import com.matrix.framework.core.i18n.I18n;
import com.matrix.framework.core.i18n.MessageConstants;
import com.matrix.framework.core.upload.FileChunkDto;
import com.matrix.framework.core.upload.FileUploadResponse;
import com.matrix.framework.core.upload.FileUploadService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

/**
 * 大文件上传示例控制器
 *
 * Copyright © 海平面工作室 版权所有
 *
 * @Author: Leo
 * @Create: 2025-08-17 12:00:00
 * @Since 1.2
 */
@RestController
@RequestMapping("/upload")
public class FileUploadController {
    
    private static final Logger logger = LoggerFactory.getLogger(FileUploadController.class);
    
    private final FileUploadService fileUploadService;
    
    @Value("${matrix.upload.complete-dir:uploads/complete}")
    private String completeDirectory;
    
    @Value("${matrix.upload.custom-dir:uploads/custom}")
    private String customDirectory;
    
    @Autowired
    public FileUploadController(FileUploadService fileUploadService) {
        this.fileUploadService = fileUploadService;
    }
    
    /**
     * 文件分片上传接口
     * 
     * @param filePart 分片文件数据
     * @param identifier 文件唯一标识符
     * @param chunkNumber 当前分片编号
     * @param totalChunks 分片总数
     * @param chunkSize 每个分片大小（字节）
     * @param totalSize 文件总大小（字节）
     * @param filename 原始文件名
     * @return 上传状态响应
     */
    @PostMapping("/chunk")
    public Mono<Result<FileUploadResponse>> uploadChunk(
            @RequestPart("file") FilePart filePart,
            @RequestParam("identifier") String identifier,
            @RequestParam("chunkNumber") Integer chunkNumber,
            @RequestParam("totalChunks") Integer totalChunks,
            @RequestParam("chunkSize") Long chunkSize,
            @RequestParam("totalSize") Long totalSize,
            @RequestParam("filename") String filename) {
        
        // 创建分片信息对象
        FileChunkDto chunkInfo = new FileChunkDto();
        chunkInfo.setIdentifier(identifier);
        chunkInfo.setChunkNumber(chunkNumber);
        chunkInfo.setTotalChunks(totalChunks);
        chunkInfo.setChunkSize(chunkSize);
        chunkInfo.setTotalSize(totalSize);
        chunkInfo.setFilename(filename);
        
        // 处理分片上传
        return fileUploadService.uploadChunk(filePart, chunkInfo)
                .map(response -> {
                    if (response.isSuccess()) {
                        return Result.ok(response)
                                .message(response.getMessage());
                    } else {
                        return Result.fail(response)
                                .message(response.getMessage());
                    }
                });
    }
    
    /**
     * 检查分片是否已上传的接口
     * 
     * @param identifier 文件唯一标识符
     * @param chunkNumber 分片编号
     * @return 分片是否存在
     */
    @GetMapping("/chunk/status")
    public Mono<Result<Boolean>> checkChunkStatus(
            @RequestParam("identifier") String identifier,
            @RequestParam("chunkNumber") Integer chunkNumber) {
        
        // 检查分片是否存在于临时目录
        return fileUploadService.checkChunkExists(identifier, chunkNumber)
                .map(exists -> Result.ok(exists)
                        .message(I18n.getMessage(MessageConstants.QUERY_SUCCESS)));
    }
    
    /**
     * 确认文件上传完成并移动到自定义目录
     * 
     * @param filename 文件名
     * @param targetDirectory 目标目录名（可选，默认为配置的自定义目录）
     * @return 操作结果
     */
    @PostMapping("/confirm")
    public Mono<Result<FileUploadResponse>> confirmUpload(
            @RequestParam("filename") String filename,
            @RequestParam(value = "targetDirectory", required = false) String targetDirectory) {
        
        return Mono.fromCallable(() -> {
            try {
                // 源文件路径
                Path sourcePath = Paths.get(completeDirectory, filename);
                
                // 如果未指定目标目录，则使用默认的自定义目录
                String targetDir = (targetDirectory != null && !targetDirectory.isEmpty()) 
                        ? customDirectory + "/" + targetDirectory 
                        : customDirectory;
                
                // 确保目标目录存在
                Path targetDirPath = Paths.get(targetDir);
                if (!Files.exists(targetDirPath)) {
                    Files.createDirectories(targetDirPath);
                }
                
                // 目标文件路径
                Path targetPath = Paths.get(targetDir, filename);
                
                // 复制文件到目标目录
                Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
                
                // 删除源文件
                Files.delete(sourcePath);
                
                logger.info("文件 {} 已成功移动到 {}", filename, targetPath);
                
                FileUploadResponse response = new FileUploadResponse(true, "文件已成功移动到自定义目录", targetPath.toString(), true);
                return Result.ok(response)
                        .message("上传成功");
            } catch (IOException e) {
                logger.error("移动文件时出错", e);
                FileUploadResponse response = new FileUploadResponse(false, "移动文件时出错: " + e.getMessage());
                return Result.fail(response)
                        .message("上传失败: " + e.getMessage());
            }
        });
    }
    
    /**
     * 获取上传文件列表
     * 
     * @return 上传文件列表
     */
    @GetMapping("/files")
    public Mono<Result<String[]>> getUploadedFiles() {
        return Mono.fromCallable(() -> {
            Path dir = Paths.get(completeDirectory);
            if (!Files.exists(dir)) {
                return new String[0];
            }
            
            return Files.list(dir)
                    .filter(Files::isRegularFile)
                    .map(path -> path.getFileName().toString())
                    .toArray(String[]::new);
        })
        .map(files -> Result.ok(files)
                .message(I18n.getMessage(MessageConstants.QUERY_SUCCESS)));
    }
}